<?php
/**
 * cache.class
 * 
 * Caching mechanizm
 * 
 * @version:	0.2b
 * @package:	core
 * @author:	alex_ez
 * @created:	21 dec 2008
 *
 * @copyright:	(c) 2009 alex <qfox@ya.ru> yaroshevich aka alex_ez. All rights reserved.
 */

if( defined( '__CACHE.CLASS_PHP' ) ) return;
define( '__CACHE.CLASS_PHP', '0.2b' );

class cache extends singleton
{
	// php singleton wrapper
	static public function &__getMe( ) { return parent::__getMe( __CLASS__ ); }
	static public function &instance() { return parent::__getMe( __CLASS__ ); }

	private $storage = array( );
	private $_dir;
	private $_enabled = true;
	private $_enables = array( );
	private $cmp = array( );
	
	protected function __construct( )
	{
		$this ->_dir = DIR_CACHE;
		$this ->_enabled = config::__getMe( ) ->cache__enabled;
	}
	
	public function enable( $k = null )
	{
		if( !is_null( $k ) && !isset( $this ->_enables[$k] ) )
			$this ->_enables[$k] = $this ->_enabled;
		
		$this ->_enabled = 1;
	}
	public function disable( $k = null )
	{
		if( !is_null( $k ) && isset( $this ->_enables[$k] ) )
		{
			$this ->_enabled = $this ->_enables[$k];
			unset( $this ->_enables[$k] );
			if( !empty( $this ->_enables ) ) return;
		}
		
		$this ->_enabled = 0;
		$this ->_enables = array( );
	}

	public function isOn( ) { return !$this ->isOff( ); }
	public function isOff( )
	{
		if( $this ->_enabled == 0 ) return true;
		if( $this ->_enabled == -1 && !defined('QFOX_PAGE_DRAWING') ) return true;
		return false;
	}



	public function getv( )
	{
		return $this ->get( null, null, null );
	}

	public function get( $class, $method, $params )
	{
		if( is_null( $class ) && is_null( $method ) && is_null( $params ) ) extract( $this ->cmp );
		
		$cache_name = $this ->get_cache_name( $class, $method, $params );
		
		if( !array_key_exists( $cache_name, $this->storage ) )
		{
			$filename = $this->get_cache_path( $class, $method, $params );
			$this->storage[ $cache_name ] = unserialize( file_get_contents( $this->_dir . $filename ) );
		}
		return $this->storage[ $cache_name ];
	}

	public function setv( &$value )
	{
		$this ->set( null, null, null, $value );
	}
	
	public function set( $class, $method, $params, &$value )
	{
		if( $this ->isOff( ) ) return;
		
		if( is_null( $class ) && is_null( $method ) && is_null( $params ) ) extract( $this ->cmp );

		$this->check_path($class, $method);
		$path = $this->get_cache_path($class, $method, $params);
		
		$data = serialize($value);
		
		$this->storage[$this->get_cache_name($class, $method, $params)] = $value;
		
		if (false !== ($cachefile = fopen($this->_dir . $path, 'w')))
		{
			flock($cachefile, LOCK_EX);
			fwrite($cachefile, $data, strlen($data));
			flock($cachefile, LOCK_UN);
			fclose($cachefile);
			
			chmod($this->_dir . $path, 0664);
		}
	}

	public function flush($class, $method, $params)
	{
		unset($this->storage[$this->get_cache_name($class, $method, $params)]);
		
		$path = $this->get_cache_path($class, $method, $params);
		
		if (file_exists($this->_dir.$path)) unlink($this->_dir.$path);
	}
	
	public function flush_all($class, $method)
	{
		$path = $this->get_cache_dir($class, $method);
		
		$dir = dir($this->_dir . $path);
		while (false !== ($entry = $dir->read())) 
		{
			if ($entry != '.' && $entry != '..')
			{
				unlink($this->_dir.$path.$entry);
				unset($this->storage[$entry]);
			}
		}
	}

	public function flushAll( $class, $methods )
	{
		if( is_array( $methods ) ) { foreach( $methods as $m ) $this ->flushAll( $class, $m ); return; }
		$method = $methods;
		
		$path = $this->_dir.$this->get_cache_dir( $class, $method );
		if( !is_dir( $path ) ) return;
		
		$dir = dir($path);
		while (false !== ($entry = $dir->read()))
		{
			if ($entry != '.' && $entry != '..')
			{
				unlink($path.$entry);
				unset($this->storage[$entry]);
			}
		}
	}
	
	public function flush_for_class($class)
	{
		$path = DIR_CACHE . $class . '/';
		
		if (is_dir($path))
		{
			exec('rm -rf ' . $path);
		}
		
		$this ->storage = array( );
	}

	public function is($class, $method, $params, $lifetime='0D')
	{
		if( $this ->isOff( ) ) return false;
		
		$this ->cmp = array( 'class' => $class, 'method' => $method, 'params' => $params );
		
		$cache_name = $this ->get_cache_name( $class, $method, $params );
		if( array_key_exists( $cache_name, $this->storage ) ) return true;
		
		$lifetime = $this ->get_time_in_seconds( $lifetime );
		
		if( $lifetime && array_key_exists($cache_name, $this->storage) )
		{
			return true;
		}
		
		$path = $this->get_cache_path( $class, $method, $params );
		
		$files_path = $this->_dir . $path;
		
		return ( file_exists($files_path)
			&& (filemtime($files_path)+$lifetime >= time()) );
	}

	private function get_time_in_seconds( $time )
	{
		list( $c, $u ) = sscanf( $time, '%u%c' ); // $count and $unit
		$u = strtolower( $u );
		$u2m = array( 'm' => 60, 'h' => 3600, 'd' => 3600*24, 'w' => 3600*24*7 ); // unit2multiplier
		return (int)(isset($u2m[$u])?($c*$u2m[$u]):$c);
	}

	private function get_cache_path(&$class, &$method, &$params)
	{
		return $this->get_cache_dir( $class, $method ) . $this->get_cache_name( $class, $method, $params );
	}
	
	private function pack_params(&$p)
	{
		if(is_scalar($p)) return is_numeric($p)?(is_int($p)?intval($p):floatval($p)):trim($p);
		if(is_object($p)) return serialize($p);
		if(!is_array($p)) return var_export($p,1);
		
		ksort($p);
		foreach( array_keys($p) as $k )
			$p[$k] = $this ->pack_params( $p[$k] );
		
		return serialize($p);
	}
	
	private function get_cache_name(&$class, &$method, $params)
	{
		return md5( $class . $method . $this ->pack_params( $params ) ) . '.cache';
	}
	
	private function get_cache_dir( $class, $method = null )
	{
		$path = array(); $path[] = $class; $path[] = str_replace( array( '::', ':' ), '-', $method );
		$path = array_filter( $path );
		
		$path = join( '/', $path ).'/';
		
		return $path;
	}
	
	private function check_path( &$class, &$method )
	{
		$cpath = $this->_dir . $this ->get_cache_dir( $class );
		$path = $this->_dir . $this ->get_cache_dir( $class, $method );
		
		if( !is_dir($path) )
		{
			mkdir( $path, 0770, true );
			chmod( $cpath, 0770 );
			chmod( $path, 0770 );
		}
	}
}


?>